//------------------------------------
//Copy from C# TimeSpan and modified time standard.
//MaxValue is The value of this field is equivalent to Int64.MaxValue ticks. The string representation of this value is positive 10675199.02:48:05.4775807.
//------------------------------------

using System;
using System.Runtime.InteropServices;

namespace Hont
{
    [ComVisible(true)]
    [Serializable]
    public struct GameTimeSpan :
        IComparable,
        IComparable<GameTimeSpan>,
        IEquatable<GameTimeSpan>
    {
        public const long MaxSeconds = 922337203685L;
        public const long MinSeconds = -922337203685L;
        public const long MaxMilliSeconds = 922337203685477L;
        public const long MinMilliSeconds = -922337203685477L;

        public static readonly GameTimeSpan Zero = new GameTimeSpan(0L);
        public static readonly GameTimeSpan MaxValue = new GameTimeSpan(9223372036854775807L);
        public static readonly GameTimeSpan MinValue = new GameTimeSpan(-9223372036854775808L);

        long _ticks;
        public long Ticks { get { return this._ticks; } }

        public int Days { get { return (int)(this._ticks / GameDateStandard.Instance.TicksPerDay); } }
        public int Hours { get { return (int)(this._ticks / GameDateStandard.Instance.TicksPerHour % GameDateStandard.Instance.HourPerDay); } }
        public int Milliseconds { get { return (int)(this._ticks / GameDateStandard.Instance.TicksPerMillisecond % GameDateStandard.Instance.MillisPerSecond); } }
        public int Minutes { get { return (int)(this._ticks / GameDateStandard.Instance.TicksPerMinute % GameDateStandard.Instance.MinutePerHour); } }
        public int Seconds { get { return (int)(this._ticks / GameDateStandard.Instance.TicksPerSecond % GameDateStandard.Instance.SecondPerMinute); } }

        public double TotalDays { get { return this._ticks / (double)GameDateStandard.Instance.TicksPerDay; } }
        public double TotalHours { get { return this._ticks / (double)GameDateStandard.Instance.TicksPerHour; } }

        public double TotalMilliseconds
        {
            get
            {
                var num = this._ticks / (double)GameDateStandard.Instance.TicksPerMillisecond;

                if (num > 922337203685477.0)
                    return MaxValue.Milliseconds;
                if (num < -922337203685477.0)
                    return -MinValue.Milliseconds;

                return num;
            }
        }

        public double TotalMinutes { get { return this._ticks / (double)GameDateStandard.Instance.TicksPerMinute; } }
        public double TotalSeconds { get { return this._ticks / (double)GameDateStandard.Instance.TicksPerSecond; } }


        public GameTimeSpan(long ticks)
        {
            this._ticks = ticks;
        }

        public GameTimeSpan(int hours, int minutes, int seconds)
        {
            this._ticks = TimeToTicks(hours, minutes, seconds);
        }

        public GameTimeSpan(int days, int hours, int minutes, int seconds)
        {
            this = new GameTimeSpan(days, hours, minutes, seconds, 0);
        }

        public GameTimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
        {
            var a = GameDateStandard.Instance.MinutePerHour * GameDateStandard.Instance.SecondPerMinute;
            var b = GameDateStandard.Instance.SecondPerMinute;
            var c = GameDateStandard.Instance.HourPerDay;

            long num = (days * a * c + hours * a + minutes * b + seconds) * GameDateStandard.Instance.MillisPerSecond + milliseconds;
            if (num > MaxMilliSeconds || num < MinMilliSeconds)
            {
                throw new ArgumentOutOfRangeException(null, "Overflow_TimeSpanTooLong");
            }
            this._ticks = num * GameDateStandard.Instance.TicksPerMillisecond;
        }

        public GameTimeSpan Add(GameTimeSpan ts)
        {
            long num = this._ticks + ts._ticks;
            if (this._ticks >> 63 == ts._ticks >> 63 && this._ticks >> 63 != num >> 63)
            {
                throw new OverflowException("Overflow_TimeSpanTooLong");
            }
            return new GameTimeSpan(num);
        }

        public static int Compare(GameTimeSpan t1, GameTimeSpan t2)
        {
            if (t1._ticks > t2._ticks)
            {
                return 1;
            }
            if (t1._ticks < t2._ticks)
            {
                return -1;
            }
            return 0;
        }

        public int CompareTo(object value)
        {
            if (value == null)
            {
                return 1;
            }
            if (!(value is TimeSpan))
            {
                throw new ArgumentException("Arg_MustBeTimeSpan");
            }
            long ticks = ((GameTimeSpan)value)._ticks;
            if (this._ticks > ticks)
            {
                return 1;
            }
            if (this._ticks < ticks)
            {
                return -1;
            }
            return 0;
        }

        public int CompareTo(GameTimeSpan value)
        {
            long ticks = value._ticks;
            if (this._ticks > ticks)
            {
                return 1;
            }
            if (this._ticks < ticks)
            {
                return -1;
            }
            return 0;
        }

        public static GameTimeSpan FromDays(double value)
        {
            return Interval(value, GameDateStandard.Instance.MillisPerDay);
        }

        public GameTimeSpan Duration()
        {
            if (this._ticks == MinValue._ticks)
            {
                throw new OverflowException("Overflow_Duration");
            }
            return new GameTimeSpan((this._ticks >= 0L) ? this._ticks : (-this._ticks));
        }

        public override bool Equals(object value)
        {
            return value is GameTimeSpan && this._ticks == ((GameTimeSpan)value)._ticks;
        }

        public bool Equals(GameTimeSpan obj)
        {
            return this._ticks == obj._ticks;
        }

        public static bool Equals(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks == t2._ticks;
        }

        public override int GetHashCode()
        {
            return (int)this._ticks ^ (int)(this._ticks >> 32);
        }

        public static GameTimeSpan FromHours(double value)
        {
            return Interval(value, GameDateStandard.Instance.MillisPerHour);
        }

        private static GameTimeSpan Interval(double value, int scale)
        {
            if (double.IsNaN(value))
            {
                throw new ArgumentException("Arg_CannotBeNaN");
            }
            double num = value * scale;
            double num2 = num + ((value >= 0.0) ? 0.5 : -0.5);
            if (num2 > 922337203685477.0 || num2 < -922337203685477.0)
            {
                throw new OverflowException("Overflow_TimeSpanTooLong");
            }
            return new GameTimeSpan((long)num2 * GameDateStandard.Instance.TicksPerMillisecond);
        }

        public static GameTimeSpan FromMilliseconds(double value)
        {
            return Interval(value, 1);
        }

        public static GameTimeSpan FromMinutes(double value)
        {
            return Interval(value, GameDateStandard.Instance.MillisPerMinute);
        }

        public GameTimeSpan Negate()
        {
            if (this._ticks == MinValue._ticks)
            {
                throw new OverflowException("Overflow_NegateTwosCompNum");
            }
            return new GameTimeSpan(-this._ticks);
        }

        public static GameTimeSpan FromSeconds(double value)
        {
            return Interval(value, GameDateStandard.Instance.MillisPerSecond);
        }

        public GameTimeSpan Subtract(GameTimeSpan ts)
        {
            long num = this._ticks - ts._ticks;
            if (this._ticks >> 63 != ts._ticks >> 63 && this._ticks >> 63 != num >> 63)
            {
                throw new OverflowException("Overflow_TimeSpanTooLong");
            }
            return new GameTimeSpan(num);
        }

        public static GameTimeSpan FromTicks(long value)
        {
            return new GameTimeSpan(value);
        }

        internal static long TimeToTicks(int hour, int minute, int second)
        {
            var a = GameDateStandard.Instance.MinutePerHour * GameDateStandard.Instance.SecondPerMinute;
            var b = GameDateStandard.Instance.SecondPerMinute;

            long num = hour * a + minute * b + second;
            if (num > MaxSeconds || num < MinSeconds)
            {
                throw new ArgumentOutOfRangeException(null, "Overflow_TimeSpanTooLong");
            }
            return num * GameDateStandard.Instance.TicksPerSecond;
        }

        public override string ToString()
        {
            if (Hours == 0) return string.Format("{0:00}:{1:00}", Minutes, Seconds);
            else if (Days == 0) return string.Format("{0:00}:{1:00}:{2:00}", Hours, Minutes, Seconds);

            return string.Format("{0}.{1:00}:{2:00}:{3:00}", Days, Hours, Minutes, Seconds);
        }

        public static GameTimeSpan operator -(GameTimeSpan t)
        {
            if (t._ticks == MinValue._ticks)
            {
                throw new OverflowException("Overflow_NegateTwosCompNum");
            }
            return new GameTimeSpan(-t._ticks);
        }

        public static GameTimeSpan operator -(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1.Subtract(t2);
        }

        public static GameTimeSpan operator +(GameTimeSpan t)
        {
            return t;
        }

        public static GameTimeSpan operator +(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1.Add(t2);
        }

        public static bool operator ==(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks == t2._ticks;
        }

        public static bool operator !=(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks != t2._ticks;
        }

        public static bool operator <(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks < t2._ticks;
        }

        public static bool operator <=(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks <= t2._ticks;
        }

        public static bool operator >(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks > t2._ticks;
        }

        public static bool operator >=(GameTimeSpan t1, GameTimeSpan t2)
        {
            return t1._ticks >= t2._ticks;
        }
    }
}
